import axios from "axios";
import CryptoJs from "crypto-js";
import * as  cheerio from "cheerio";
import he from "he";
import dayjs from "dayjs";
import qs from "qs";
import bigInt from "big-integer";
import { IPluginManager, PluginState, PluginErrorReason, IPluginDefine, IPluginInstance, IPluginInstanceMethods, ISearchResult } from "./def";
import { SupportVideoType, VideoIntroduceInfo, VideoDetailInfo } from "./icommon";
import https from "https";
export const appVersion = "1.0.0";

export const mAxios = axios.create({
    timeout: 10000,
    httpsAgent: new https.Agent({
        rejectUnauthorized: false,
    }),
    headers: {
        "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1",
    },
});

const sha256 = CryptoJs.SHA256;

const packages: Record<string, any> = {
    cheerio,
    "crypto-js": CryptoJs,
    axios: mAxios,
    dayjs,
    "big-integer": bigInt,
    qs,
    he,
    https,
};

const _require = (packageName: string) => {

    let pkg = packages[packageName];
    pkg.default = pkg;
    return pkg;
};

export class IPlugin {
    /** 插件名 */
    public name: string;
    /** 插件的hash，作为唯一id */
    public hash: string;
    /** 插件状态：激活、关闭、错误 */
    public state: PluginState = PluginState.Loading;
    /** 插件出错时的原因 */
    public errorReason?: PluginErrorReason;
    /** 插件的实例 */
    public instance: IPluginDefine;
    /** 插件路径 */
    public path: string;

    /** 插件是否启用 */
    public enabled: boolean = false;

    /** 插件方法 */
    public methods: IPluginInstanceMethods;

    static pluginManager: IPluginManager;


    static injectDependencies(
        pluginManager: IPluginManager,
    ) {
        IPlugin.pluginManager = pluginManager;
    }

    constructor(funcCode: string | (() => IPluginDefine),
        pluginPath: string,) {
        let _instance: IPluginDefine;

        const _module: any = { exports: {} };
        try {
            if (typeof funcCode === "string") {
                // 插件的环境变量
                const env = {

                    appVersion,
                    os: "android",
                    lang: "zh-CN",
                };
                const _process = {
                    platform: "android",
                    version: appVersion,
                    env,
                };

                // eslint-disable-next-line no-new-func
                _instance = Function(`
                    'use strict';
                    return function(require, __musicfree_require, module, exports, console, env, URL, process) {
                        ${funcCode}
                    }
                `)()(
                    _require,
                    _require,
                    _module,
                    _module.exports,
                    console,
                    env,
                    URL,
                    _process
                );
                if (_module.exports.default) {
                    _instance = _module.exports
                        .default as IPluginInstance;
                } else {
                    _instance = _module.exports as IPluginInstance;
                }
            } else {
                _instance = funcCode();
            }

        } catch (e: any) {
            this.state = PluginState.Error;
            this.errorReason = e?.errorReason ?? PluginErrorReason.CannotParse;

            console.error(`${pluginPath}插件无法解析 `, {
                errorReason: this.errorReason,
                message: e?.message,
                stack: e?.stack,
            });
            _instance = e?.instance ?? {
                platform: "",
                appVersion: "",
                async fetchIntroduceInfo() {
                    return null;
                },
                async search() {
                    return {};
                },
                async fetchVideoDetailInfo() {
                    return null;
                },
            };
        }

        this.instance = _instance;
        this.path = pluginPath;
        this.name = _instance.platform;

        // 检测name & 计算hash
        if (
            this.name === "" ||
            !this.name
        ) {
            this.hash = "";
            this.state = PluginState.Error;
            this.errorReason = this.errorReason ?? PluginErrorReason.CannotParse;
        } else {
            if (typeof funcCode === "string") {
                this.hash = sha256(funcCode).toString();
            } else {
                this.hash = sha256(pluginPath + "@" + appVersion).toString();
            }
        }


        if (this.state !== PluginState.Error) {
            this.state = PluginState.Mounted;
        }

        this.methods = new IPluginInstanceMethodsWraper(this);

    }
}

class IPluginInstanceMethodsWraper implements IPluginInstanceMethods {
    private _plugin: IPlugin;
    constructor(plugin: IPlugin) {
        this._plugin = plugin;
    }


    async search<T extends SupportVideoType>(query: string, page: number, type: T): Promise<ISearchResult<T>> {
        if (!this._plugin.instance.search) {
            return {
                isEnd: true,
                data: []
            }
        }
        if (type === "all" && this._plugin.instance.enabledSearch == false) {
            return {
                isEnd: true,
                data: []
            }
        }
        const result =
            (await this._plugin.instance.search(query, page, type)) ?? {};
        return result;
    }
    async fetchIntroduceInfo(params: {
        url: string
        id: string
    }): Promise<VideoIntroduceInfo> {
        if (!this._plugin.instance.fetchIntroduceInfo) {
            return {} as VideoIntroduceInfo;
        }
        return (await this._plugin.instance.fetchIntroduceInfo(params)) ?? {} as VideoIntroduceInfo;
    }
    async fetchVideoDetailInfo(params: {
        url: string
        id: string,
        source?: number,
        episodeIndex?: number
    }): Promise<VideoDetailInfo> {
        if (!this._plugin.instance.fetchVideoDetailInfo) {
            return {} as VideoDetailInfo;
        }
        return (await this._plugin.instance.fetchVideoDetailInfo(params)) ?? {} as VideoDetailInfo;
    }

}